﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Snowdreamist.Collection.Tree
{
    public abstract class TreeNodeBase<T> : ITreeNode<T> where T : TreeNodeBase<T>
    {
        public TreeNodeBase()
        {
            this.Children = this.CreateCollection();
            if (this.Children == null)
                throw new InvalidOperationException("CreateCollection returns null");
        }

        public virtual ITreeNodeNavigator<T> CreateNavigator()
        {
            return new TreeNodeNavigator<T>((T)this);
        }

        public T Parent { get; private set; }

        public ITreeNodeCollection<T> Children { get; private set; }

        public virtual T Root
        {
            get
            {
                T curNode = (T)this;

                while (curNode.Parent != null)
                    curNode = curNode.Parent;

                return curNode;
            }
        }

        public virtual bool TreeNodeEquals(ITreeNode<T> other)
        {
            if (other == null)
                return false;
            if (object.ReferenceEquals(this, other))
                return true;
            // compare the children
            if (this.Children.Count != other.Children.Count)
                return false;
            for (int i = 0; i < this.Children.Count; ++i)
                if (!(this.Children[i].TreeNodeEquals(other.Children[i])))
                    return false;
            return true;
        }

        public virtual bool IsSameRoot(ITreeNode<T> other)
        {
            if (other == null)
                return false;
            return object.ReferenceEquals(this.Root, other.Root);
        }

        public virtual void Attach(ITreeNode<T> parent)
        {
            if (this.IsAttached)
                throw new InvalidOperationException("This node has already attached");
            this.Parent = (T)parent;
            if (this.Parent != null)
                this.IsAttached = true;
        }

        public virtual bool Detach()
        {
            if (!this.IsAttached)
                return false;
            this.Parent = null;
            return true;
        }

        public bool IsAttached { get; private set; }

        public override string ToString()
        {
            StringBuilder strBuilder = new StringBuilder();
            this.ToString(0, strBuilder);
            return strBuilder.ToString();
        }

        public virtual void ToString(int indent, StringBuilder strBuilder)
        {
            foreach (var child in this.Children)
                child.ToString(indent + 1, strBuilder);
        }

        protected abstract ITreeNodeCollection<T> CreateCollection();
    }
}